1 using System;
2 using
UnityEngine.Events;
3 using
UnityEngine.EventSystems;
4
5 namespace
UnityEngine.UI.Extensions
6 {
7     
public class ScrollSnapBase : MonoBehaviour, IBeginDragHandler, IDragHandler
8     {
9         
internal RectTransform _screensContainer;
10         
internal bool _isVertical;
11
12         
internal int _screens = 1;
13
14         
internal float _scrollStartPosition;
15         
internal float _childSize;
16         
private float _childPos, _maskSize;
17         
internal Vector2 _childAnchorPoint;
18         
internal ScrollRect _scroll_rect;
19         
internal Vector3 _lerp_target;
20         
internal bool _lerp;
21         
internal bool _pointerDown = false;
22         
internal bool _settled = true;
23         
internal Vector3 _startPosition = new Vector3();
24         
[Tooltip("The currently active page")]
25         
internal int _currentPage;
26         
internal int _previousPage;
27         
internal int _halfNoVisibleItems;
28         
private int _bottomItem, _topItem;
29
30         
[Serializable]
31         
public class SelectionChangeStartEvent : UnityEvent { }
32         
[Serializable]
33         
public class SelectionPageChangedEvent : UnityEvent<int> { }
34         
[Serializable]
35         
public class SelectionChangeEndEvent : UnityEvent<int> { }
36
37         
[Tooltip("The screen / page to start the control on\n*Note, this is a 0 indexed array")]
38         
[SerializeField]
39         
public int StartingScreen = 0;
40
41         
[Tooltip("The distance between two pages based on page height, by default pages are next to each other")]
42         
[SerializeField]
43         
[Range(0, 8)]
44         
public float PageStep = 1;
45
46         
[Tooltip("The gameobject that contains toggles which suggest pagination. (optional)")]
47         
public GameObject Pagination;
48
49         
[Tooltip("Button to go to the previous page. (optional)")]
50         
public GameObject PrevButton;
51
52         
[Tooltip("Button to go to the next page. (optional)")]
53         
public GameObject NextButton;
54
55         
[Tooltip("Transition speed between pages. (optional)")]
56         
public float transitionSpeed = 7.5f;
57
58         
[Tooltip("Fast Swipe makes swiping page next / previous (optional)")]
59         
public Boolean UseFastSwipe = false;
60
61         
[Tooltip("How far swipe has to travel to initiate a page change (optional)")]
62         
public int FastSwipeThreshold = 100;
63
64         
[Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")]
65         
public int SwipeVelocityThreshold = 200;
66
67         
[Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")]
68         
public RectTransform MaskArea;
69
70         
[Tooltip("Pixel size to buffer arround Mask Area. (optional)")]
71         
public float MaskBuffer = 1;
72
73         
public int CurrentPage
74         {
75             
get
76             {
77                 
return _currentPage;
78             }
79
80             
internal set
81             {
82                 
if ((value != _currentPage && value >= 0 && value < _screensContainer.childCount) || (value == 0 && _screensContainer.childCount == 0))
83                 {
84                     _previousPage = _currentPage;
85                     _currentPage =
value;
86                     
if(MaskArea) UpdateVisible();
87                     
if(!_lerp) ScreenChange();
88                     OnCurrentScreenChange(_currentPage);
89                 }
90             }
91         }
92
93         
[Tooltip("(Experimental)\nBy default, child array objects will use the parent transform\nHowever you can disable this for some interesting effects")]
94         
public bool UseParentTransform = true;
95
96         
[Tooltip("Scroll Snap children. (optional)\nEither place objects in the scene as children OR\nPrefabs in this array, NOT BOTH")]
97         
public GameObject[] ChildObjects;
98
99         
[SerializeField]
100         
[Tooltip("Event fires when a user starts to change the selection")]
101         
private SelectionChangeStartEvent m_OnSelectionChangeStartEvent = new SelectionChangeStartEvent();
102         
public SelectionChangeStartEvent OnSelectionChangeStartEvent { get { return m_OnSelectionChangeStartEvent; } set { m_OnSelectionChangeStartEvent = value; } }
103
104         
[SerializeField]
105         
[Tooltip("Event fires as the page changes, while dragging or jumping")]
106         
private SelectionPageChangedEvent m_OnSelectionPageChangedEvent = new SelectionPageChangedEvent();
107         
public SelectionPageChangedEvent OnSelectionPageChangedEvent { get { return m_OnSelectionPageChangedEvent; } set { m_OnSelectionPageChangedEvent = value; } }
108
109         
[SerializeField]
110         
[Tooltip("Event fires when the page settles after a user has dragged")]
111         
private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent();
112         
public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } }
113
114         
// Use this for initialization
115         
void Awake()
116         {
117             _scroll_rect = gameObject.GetComponent<ScrollRect>();
118
119             
if (_scroll_rect.horizontalScrollbar || _scroll_rect.verticalScrollbar)
120             {
121                 Debug.LogWarning(
"Warning, using scrollbars with the Scroll Snap controls is not advised as it causes unpredictable results");
122             }
123
124             
if (StartingScreen < 0)
125             {
126                 StartingScreen =
0;
127             }
128
129             _screensContainer = _scroll_rect.content;
130             
if (ChildObjects != null && ChildObjects.Length > 0)
131             {
132                 
if (_screensContainer.transform.childCount > 0)
133                 {
134                     Debug.LogError(
"ScrollRect Content has children, this is not supported when using managed Child Objects\n Either remove the ScrollRect Content children or clear the ChildObjects array");
135                     
return;
136                 }
137
138                 InitialiseChildObjectsFromArray();
139             }
140             
else
141             {
142                 InitialiseChildObjectsFromScene();
143             }
144
145             
if (NextButton)
146                 NextButton.GetComponent<Button>().onClick.AddListener(() => { NextScreen(); });
147
148             
if (PrevButton)
149                 PrevButton.GetComponent<Button>().onClick.AddListener(() => { PreviousScreen(); });
150             }
151
152         
internal void InitialiseChildObjectsFromScene()
153         {
154             
int childCount = _screensContainer.childCount;
155             ChildObjects =
new GameObject[childCount];
156             
for (int i = 0; i < childCount; i++)
157             {
158                 ChildObjects[i] = _screensContainer.transform.GetChild(i).gameObject;
159                 
if (MaskArea && ChildObjects[i].activeSelf)
160                 {
161                     ChildObjects[i].SetActive(
false);
162                 }
163             }
164         }
165
166         
internal void InitialiseChildObjectsFromArray()
167         {
168             
int childCount = ChildObjects.Length;
169             RectTransform childRect;
170             GameObject child;
171             
for (int i = 0; i < childCount; i++)
172             {
173                 child = GameObject.Instantiate(ChildObjects[i]);
174                 
//Optionally, use original GO transform when initialising, by default will use parent RectTransform position/rotation
175                 
if (UseParentTransform)
176                 {
177                     childRect = child.GetComponent<RectTransform>();
178                     childRect.rotation = _screensContainer.rotation;
179                     childRect.localScale = _screensContainer.localScale;
180                     childRect.position = _screensContainer.position;
181                 }
182
183                 child.transform.SetParent(_screensContainer.transform);
184                 ChildObjects[i] = child;
185                 
if (MaskArea && ChildObjects[i].activeSelf)
186                 {
187                     ChildObjects[i].SetActive(
false);
188                 }
189             }
190         }
191
192         
internal void UpdateVisible()
193         {
194             
//If there are no objects in the scene or a mask, exit
195             
if (!MaskArea || ChildObjects == null || ChildObjects.Length < 1 || _screensContainer.childCount < 1)
196             {
197                 
return;
198             }
199
200             _maskSize = _isVertical ? MaskArea.rect.height : MaskArea.rect.width;
201             _halfNoVisibleItems = (
int)Math.Round(_maskSize / (_childSize * MaskBuffer), MidpointRounding.AwayFromZero) / 2;
202             _bottomItem = _topItem =
0;
203             
//work out how many items below the current page can be visible
204             
for (int i = _halfNoVisibleItems + 1; i > 0; i--)
205             {
206                 _bottomItem = _currentPage - i <
0 ? 0 : i;
207                 
if (_bottomItem > 0) break;
208             }
209
210             
//work out how many items above the current page can be visible
211             
for (int i = _halfNoVisibleItems + 1; i > 0; i--)
212             {
213                 _topItem = _screensContainer.childCount - _currentPage - i <
0 ? 0 : i;
214                 
if (_topItem > 0) break;
215             }
216
217             
//Set the active items active
218             
for (int i = CurrentPage - _bottomItem; i < CurrentPage + _topItem; i++)
219             {
220                 
try
221                 {
222                     ChildObjects[i].SetActive(
true);
223                 }
224                 
catch
225                 {
226                     Debug.Log(
"Failed to setactive child [" + i + "]");
227                 }
228             }
229
230             
//Deactivate items out of visibility at the bottom of the ScrollRect Mask (only on scroll)
231             
if (_currentPage > _halfNoVisibleItems) ChildObjects[CurrentPage - _bottomItem].SetActive(false);
232             
//Deactivate items out of visibility at the top of the ScrollRect Mask (only on scroll)
233             
if (_screensContainer.childCount - _currentPage > _topItem) ChildObjects[CurrentPage + _topItem].SetActive(false);
234         }
235
236         
//Function for switching screens with buttons
237         
public void NextScreen()
238         {
239             
if (_currentPage < _screens - 1)
240             {
241                 
if (!_lerp) StartScreenChange();
242
243                 _lerp =
true;
244                 CurrentPage = _currentPage +
1;
245                 GetPositionforPage(_currentPage,
ref _lerp_target);
246                 ScreenChange();
247             }
248         }
249
250         
//Function for switching screens with buttons
251         
public void PreviousScreen()
252         {
253             
if (_currentPage > 0)
254             {
255                 
if (!_lerp) StartScreenChange();
256
257                 _lerp =
true;
258                 CurrentPage = _currentPage -
1;
259                 GetPositionforPage(_currentPage,
ref _lerp_target);
260                 ScreenChange();
261             }
262         }

263
264         ///
<summary>
265         ///
Function for switching to a specific screen
266         ///
*Note, this is based on a 0 starting index - 0 to x
267         ///
</summary>
268         ///
<param name="screenIndex">0 starting index of page to jump to</param>
269         
public void GoToScreen(int screenIndex)
270         {
271             
if (screenIndex <= _screens - 1 && screenIndex >= 0)
272             {
273                 
if (!_lerp) StartScreenChange();
274
275                 _lerp =
true;
276                 CurrentPage = screenIndex;
277                 GetPositionforPage(_currentPage,
ref _lerp_target);
278                 ScreenChange();
279             }
280         }

281
282         ///
<summary>
283         ///
Gets the closest page for the current Scroll Rect container position
284         ///
</summary>
285         ///
<param name="pos">Position to test, normally the Scroll Rect container Local position</param>
286         ///
<returns>Closest Page number (zero indexed array value)</returns>
287         
internal int GetPageforPosition(Vector3 pos)
288         {
289             
return _isVertical ?
290                 -(
int)Math.Round((pos.y - _scrollStartPosition) / _childSize) :
291                 -(
int)Math.Round((pos.x - _scrollStartPosition) / _childSize);
292         }

293
294         ///
<summary>
295         ///
Validates if the current Scroll Rect container position is within the bounds for a page
296         ///
</summary>
297         ///
<param name="pos">Position to test, normally the Scroll Rect container Local position</param>
298         ///
<returns>True / False, is the position in the bounds of a page</returns>
299         
internal bool IsRectSettledOnaPage(Vector3 pos)
300         {
301             
return _isVertical ?
302                 -((pos.y - _scrollStartPosition) / _childSize) == -(
int)Math.Round((pos.y - _scrollStartPosition) / _childSize) :
303                 -((pos.x - _scrollStartPosition) / _childSize) == -(
int)Math.Round((pos.x - _scrollStartPosition) / _childSize);
304         }

305
306         ///
<summary>
307         ///
Returns the local position for a child page based on the required page number
308         ///
</summary>
309         ///
<param name="page">Page that the position is required for (Zero indexed array value)</param>
310         ///
<param name="target">Outputs the local position for the selected page</param>
311         
internal void GetPositionforPage(int page, ref Vector3 target)
312         {
313             _childPos = -_childSize * page;
314             
if (_isVertical)
315             {
316                 target.y = _childPos + _scrollStartPosition;
317             }
318             
else
319             {
320                 target.x = _childPos + _scrollStartPosition;
321             }
322         }

323
324         ///
<summary>
325         ///
Updates the _Lerp target to the closest page and updates the pagination bullets. Each control's update loop will then handle the move.
326         ///
</summary>
327         
internal void ScrollToClosestElement()
328         {
329             _lerp =
true;
330             CurrentPage = GetPageforPosition(_screensContainer.localPosition);
331             GetPositionforPage(_currentPage,
ref _lerp_target);
332             OnCurrentScreenChange(_currentPage);
333         }

334
335         ///
<summary>
336         ///
notifies pagination indicator and navigation buttons of a screen change
337         ///
</summary>
338         
internal void OnCurrentScreenChange(int currentScreen)
339         {
340             ChangeBulletsInfo(currentScreen);
341             ToggleNavigationButtons(currentScreen);
342         }

343
344         ///
<summary>
345         ///
changes the bullets on the bottom of the page - pagination
346         ///
</summary>
347         ///
<param name="targetScreen"></param>
348         
private void ChangeBulletsInfo(int targetScreen)
349         {
350             
if (Pagination)
351                 
for (int i = 0; i < Pagination.transform.childCount; i++)
352                 {
353                     Pagination.transform.GetChild(i).GetComponent<Toggle>().isOn = (targetScreen == i)
354             ?
true
355                         :
false;
356         }
357     }

358
359     ///
<summary>
360     ///
disables the page navigation buttons when at the first or last screen
361     ///
</summary>
362     ///
<param name="targetScreen"></param>
363         
private void ToggleNavigationButtons(int targetScreen) {
364         
if (PrevButton) {
365             PrevButton.GetComponent<Button>().interactable = targetScreen >
0;
366         }
367
368         
if (NextButton) {
369             NextButton.GetComponent<Button>().interactable = targetScreen < _screensContainer.transform.childCount -
1;
370         }
371     }
372
373     
private void OnValidate()
374     {
375         
var children = gameObject.GetComponent<ScrollRect>().content.childCount;
376         
if (children != 0 || ChildObjects != null)
377         {
378             
var childCount = ChildObjects == null || ChildObjects.Length == 0 ? children : ChildObjects.Length;
379             
if (StartingScreen > childCount - 1)
380             {
381                 StartingScreen = childCount -
1;
382             }
383
384             
if (StartingScreen < 0)
385             {
386                 StartingScreen =
0;
387             }
388         }
389
390         
if (MaskBuffer <= 0)
391         {
392             MaskBuffer =
1;
393         }
394
395         
if (PageStep < 0)
396         {
397             PageStep =
0;
398         }
399
400         
if (PageStep > 8)
401         {
402             PageStep =
9;
403         }
404     }

405
406     ///
<summary>
407     ///
Event fires when the user starts to change the page, either via swipe or button.
408     ///
</summary>
409         
internal void StartScreenChange()
410     {
411         OnSelectionChangeStartEvent.Invoke();
412     }

413
414     ///
<summary>
415     ///
Event fires when the currently viewed page changes, also updates while the scroll is moving
416     ///
</summary>
417         
internal void ScreenChange()
418     {
419         OnSelectionPageChangedEvent.Invoke(_currentPage);
420     }

421
422     ///
<summary>
423     ///
Event fires when control settles on a page, outputs the new page number
424     ///
</summary>
425         
internal void EndScreenChange()
426     {
427         OnSelectionChangeEndEvent.Invoke(_currentPage);
428         _settled =
true;
429     }
430
431     
#region Interfaces
432     ///
<summary>
433     ///
Touch screen to start swiping
434     ///
</summary>
435     ///
<param name="eventData"></param>
436         
public void OnBeginDrag(PointerEventData eventData)
437     {
438         _pointerDown =
true;
439         _settled =
false;
440         StartScreenChange();
441         _startPosition = _screensContainer.localPosition;
442     }

443
444     ///
<summary>
445     ///
While dragging do
446     ///
</summary>
447     ///
<param name="eventData"></param>
448         
public void OnDrag(PointerEventData eventData)
449     {
450         _lerp =
false;
451     }
452
453     
#endregion
454 }
455 }


Use this for initialization

Optionally, use original GO transform when initialising, by default will use parent RectTransform positionrotation

If there are no objects in the scene or a mask, exit

work out how many items below the current page can be visible

work out how many items above the current page can be visible

Set the active items active

Deactivate items out of visibility at the bottom of the ScrollRect Mask (only on scroll)

Deactivate items out of visibility at the top of the ScrollRect Mask (only on scroll)

Function for switching screens with buttons

Function for switching screens with buttons

Function for switching to a specific screen

*Note, this is based on a 0 starting index - 0 to x

0 starting index of page to jump to

Gets the closest page for the current Scroll Rect container position

Position to test, normally the Scroll Rect container Local position

Closest Page number (zero indexed array value)

Validates if the current Scroll Rect container position is within the bounds for a page

Position to test, normally the Scroll Rect container Local position

True False, is the position in the bounds of a page

Returns the local position for a child page based on the required page number

Page that the position is required for (Zero indexed array value)

Outputs the local position for the selected page

Updates the _Lerp target to the closest page and updates the pagination bullets. Each control's update loop will then handle the move.

notifies pagination indicator and navigation buttons of a screen change

changes the bullets on the bottom of the page - pagination

disables the page navigation buttons when at the first or last screen

Event fires when the user starts to change the page, either via swipe or button.

Event fires when the currently viewed page changes, also updates while the scroll is moving

Event fires when control settles on a page, outputs the new page number

Touch screen to start swiping

While dragging do




Trò chơi đua xe động vật trong UNITY Engine 114.843 lượt xem

Gõ tìm kiếm nhanh...